#include "flash.h"

u8 W25_BUFFER[4096]; // flash缓冲区大小定义

#define SPI_CS_LOW() LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_4)
#define SPI_CS_HIGH() LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_4)

static void spi_config(void)
{
    LL_SPI_InitTypeDef SPI_InitStruct = {0};

    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* Enable clock */
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);

    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
    /** SPI1 Pin config
    PA5   ------> SPI1_SCK
    PA6   ------> SPI1_MISO
    PA7   ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_5;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_3;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_3;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_3;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* SPI1 parameter configuration*/
    SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
    SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
    SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
    SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_HIGH;
    SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
    SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
    SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV4;
    SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
    SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
    SPI_InitStruct.CRCPoly = 0x0U;
    LL_SPI_Init(SPI1, &SPI_InitStruct);

    LL_SPI_Enable(SPI1);
}

u8 SPI1_ReadWriteByte(u8 TxData)
{
    /* loop while data register in not emplty */
    while (0 == LL_SPI_IsActiveFlag_TXE(SPI1))
        ;

    /* send byte through the SPI0 peripheral */
    LL_SPI_TransmitData8(SPI1, TxData);

    /* wait to receive a byte */
    while (0 == LL_SPI_IsActiveFlag_RXNE(SPI1))
        ;

    /* return the byte read from the SPI bus */
    return LL_SPI_ReceiveData8(SPI1);    
}

/***********************************************************************************************
**     name: flash_init
** function: 初始化flash W25N01GV模块
**parameter: void
**   return: void
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
void flash_init(void)
{
    u16 id = 0;

    spi_config();  // 初始化spi1
    SPI_CS_HIGH(); // 拉高cs片选
    SPI1_ReadWriteByte(0x10);
    W25_Reset();
    LL_mDelay(20);
    W25_WriteSR(Protection_Register, 0); // SR1取消保护
    LL_mDelay(20);

    id = W25_ReadID();
    rt_kprintf("flash id:%d\n", id);
}
//INIT_BOARD_EXPORT(flash_init);


/***********************************************************************************************
**     name: W25_ReadSR
** function: 读取W25的状态寄存器
                         BIT7   6      5     4     3       2       1    0
             NULL   LUT_F  ECC1  ECC0  P_FAIL  E_FAIL  WEL  BUSY
             WEL:写使能锁定
             BUSY:忙标记位(1,忙;0,空闲)
             默认:0x00
**parameter: sr 寄存器地址
**   return: 寄存器的值
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
u8 W25_ReadSR(u8 sr)
{

    u8 byte = 0;

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_ReadStatusReg); // 发送读取状态寄存器命令
    SPI1_ReadWriteByte(sr);                 // 发送地址
    byte = SPI1_ReadWriteByte(0Xff);        // 读取一个字节

    SPI_CS_HIGH(); // 取消片选

    return byte;
}

/***********************************************************************************************
**     name: W25_WriteSR
** function: 写W25状态寄存器,只有部分位可以写!!!
**parameter: sr 寄存器地址
                         data 欲写入的值
**   return: void
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
void W25_WriteSR(u8 sr, u8 data)
{

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_WriteStatusReg); // 发送写状态寄存器命令
    SPI1_ReadWriteByte(sr);                  // 发送地址
    SPI1_ReadWriteByte(data);                // 发送欲写入的值

    SPI_CS_HIGH(); // 取消片选
}

/***********************************************************************************************
**     name: W25_Reset
** function: 重启flash
**parameter: void
**   return: void
**     date: 2019/8/30
**   create: @曼珠沙华
************************************************************************************************/
void W25_Reset(void)
{

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_Reset); // 发送写状态寄存器命令

    SPI_CS_HIGH(); // 取消片选
}

/***********************************************************************************************
**     name: W25_Write_Enable
** function: W25写使能，将WEL置位
**parameter: void
**   return: void
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
void W25_Write_Enable(void)
{

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_WriteEnable); // 发送写使能

    SPI_CS_HIGH(); // 取消片选
}

/***********************************************************************************************
**     name: W25_Write_Disable
** function: W25写禁止，将WEL清零
**parameter: void
**   return: void
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
void W25_Write_Disable(void)
{

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_WriteDisable); // 发送写禁止指令

    SPI_CS_HIGH(); // 取消片选
}

/***********************************************************************************************
**     name: W25_ReadID
** function: 读取芯片ID
**parameter: void
**   return: 返回值如下:
                         0XAA21,表示芯片型号为W25N01GV
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
u16 W25_ReadID(void)
{

    u32 Temp = 0;

    SPI_CS_LOW();

    SPI1_ReadWriteByte(W25X_JedecDeviceID); // 发送读取ID命令
    SPI1_ReadWriteByte(0x00);
    Temp |= SPI1_ReadWriteByte(0xFF) << 16;
    Temp |= SPI1_ReadWriteByte(0xFF) << 8;
    Temp |= SPI1_ReadWriteByte(0xFF);

    SPI_CS_HIGH();

    return Temp;
}

/***********************************************************************************************
**     name: W25_PageDataRead
** function: 读取指定页地址的数据，并移动到缓冲区
**parameter: BlockAddr 写入的块地址（0-1023）
             PageAddr  写入的页地址（0-63）
**   return: void
**     date: 2019/8/29
**   create: @曼珠沙华
************************************************************************************************/
void W25_PageDataRead(u16 BlockAddr, u8 PageAddr)
{

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(PageDataRead);                  // 发送读取命令
    SPI1_ReadWriteByte(0);                             // 8-bit dummy clocks
    SPI1_ReadWriteByte((u8)((BlockAddr >> 2) & 0xFF)); // 发送16bit地址
    SPI1_ReadWriteByte((u8)((BlockAddr << 6) | PageAddr) & 0xFF);

    SPI_CS_HIGH();
}

/***********************************************************************************************
**     name: W25_Read
** function: 读取SPI FLASH，在指定地址开始读取指定长度的数据
**parameter: pBuffer 数据存储区
             BytesAddr 写入的具体字节地址(0-1023)
                         BlockAddr 写入的块地址（0-1023）
             PageAddr  写入的页地址（0-63）
             NumByteToWrite 要写入的数据个数(0-65535),该数不应该超过该页的剩余字节数!!!
**   return: void
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
void W25_Read(u8* pBuffer, u16 BytesAddr, u16 BlockAddr, u8 PageAddr, u16 NumByteToRead)
{

    u16 i;

    W25_PageDataRead(BlockAddr, PageAddr); // 先读取页地址数据，送到缓冲区
    LL_mDelay(100);

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_ReadData);                 // 发送读取命令
    SPI1_ReadWriteByte((u8)((BytesAddr >> 8) & 0x07)); // 发送16位地址
    SPI1_ReadWriteByte((u8)BytesAddr & 0xFF);
    SPI1_ReadWriteByte(0); // 8-bit dummy clocks
    for (i = 0; i < NumByteToRead; i++)
    {
        pBuffer[i] = SPI1_ReadWriteByte(0); // 循环读数
    }

    SPI_CS_HIGH();
}

/***********************************************************************************************
**     name: W25_WriteBuff
** function: 将数据写入W25的数据缓冲区，数据不超过2048byte
**parameter: CA_add         写入的具体字节地址(0-2047)
                         pBuffer        欲写入的数据地址
                         NumByteToWrite 要写入的数据个数(0-65535),该数不应该超过该页的剩余字节数!!!
**   return: void
**     date: 2019/8/29
**   create: @曼珠沙华
************************************************************************************************/
void W25_WriteBuff(u16 CA_add, u8* pBuffer, u16 NumByteToWrite)
{

    u16 i;

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_PageProgram);           // 发送写缓冲区命令
    SPI1_ReadWriteByte((u8)((CA_add >> 8) & 0x07)); // 发送16bit地址
    SPI1_ReadWriteByte((u8)CA_add & 0xFF);

    for (i = 0; i < NumByteToWrite; i++)
    {
        SPI1_ReadWriteByte(pBuffer[i]); // 循环写数
    }
    SPI_CS_HIGH(); // 取消片选
}

/***********************************************************************************************
**     name: W25_WriteData
** function: SPI在一页(0~65535)内写入少于256个字节的数据
                         在指定地址开始写入最大256字节的数据
**parameter: pBuffer 数据存储区
             BytesAddr 写入的具体字节地址(0-1023)
                         BlockAddr 写入的块地址（0-1023）
             PageAddr  写入的页地址（0-63）
             NumByteToWrite 要写入的数据个数(0-65535),该数不应该超过该页的剩余字节数!!!
**   return: void
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
void W25_WriteData(u8* pBuffer, u16 BytesAddr, u16 BlockAddr, u8 PageAddr, u16 NumByteToWrite)
{

    W25_Write_Enable(); // SET WEL
    LL_mDelay(20);
    W25_WriteBuff(BytesAddr, pBuffer, NumByteToWrite); // 先写到buff
    LL_mDelay(20);

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_ProgramExecute);           // 发送写命令
    SPI1_ReadWriteByte(0);                             // 8-bit dummy clocks
    SPI1_ReadWriteByte((u8)((BlockAddr >> 2) & 0xFF)); // 发送16bit地址
    SPI1_ReadWriteByte((u8)((BlockAddr << 6) | PageAddr) & 0xFF);

    SPI_CS_HIGH();   // 取消片选
    W25_Wait_Busy(); // 等待写入结束
    W25_Write_Disable();
    LL_mDelay(20);
}

/***********************************************************************************************
**     name: W25_Erase_Sector
** function: 擦除一个块(128kb)
**parameter: Dst_Addr 块地址（0-1023）
**   return: void
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
void W25_Erase_Sector(u16 Dst_Addr)
{

    W25_Write_Enable(); // SET WEL
    SPI_CS_LOW();       // 使能器件

    SPI1_ReadWriteByte(W25X_BlockErase);              // 发送块擦除指令
    SPI1_ReadWriteByte(0);                            // 8-bit dummy clocks
    SPI1_ReadWriteByte((u8)((Dst_Addr >> 2) & 0xFF)); // 发送16bit地址
    SPI1_ReadWriteByte((u8)((Dst_Addr << 6) & 0xC0));

    SPI_CS_HIGH();   // 取消片选
    W25_Wait_Busy(); // 等待擦除完成
    W25_Write_Disable();
}

/***********************************************************************************************
**     name: W25_Wait_Busy
** function: 等待空闲
**parameter: void
**   return: void
**     date: 2019/8/27
**   create: @曼珠沙华
************************************************************************************************/
void W25_Wait_Busy(void)
{

    while ((W25_ReadSR(Status_Register) & 0x01) == 0x01)
        ; // 等待BUSY位清空
}

/***********************************************************************************************
**     name: W25_PowerDown
** function: 进入掉电模式
**parameter: void
**   return: void
**     date: 2019/8/28
**   create: @曼珠沙华
************************************************************************************************/
void W25_PowerDown(void)
{

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_PowerDown); // 发送掉电命令

    SPI_CS_HIGH(); // 取消片选
}

/***********************************************************************************************
**     name: W25_WAKEUP
** function: 唤醒
**parameter: void
**   return: void
**     date: 2019/8/28
**   create: @曼珠沙华
************************************************************************************************/
void W25_WAKEUP(void)
{

    SPI_CS_LOW(); // 使能器件

    SPI1_ReadWriteByte(W25X_ReleasePowerDown); //  send W25X_PowerDown command 0xAB

    SPI_CS_HIGH(); // 取消片选
}
